package httpx

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
	"time"

	"gitee.com/EagleMao/eino_n9e_tool/pkg/logger"
	"github.com/astaxie/beego/httplib"
)

type HttpClient struct {
	BaseUrl string            `json:"base_url"`
	Timeout time.Duration     `json:"timeout"`
	Headers map[string]string `json:"headers"`
}

type RequestOptions struct {
	Headers     map[string]string
	QueryParams map[string]string
	FormParams  map[string]string
	Timeout     time.Duration
}

var (
	ErrInvalidMethod = errors.New("invalid http method")
)

func NewHttpX(baseURL string) *HttpClient {
	return &HttpClient{
		BaseUrl: baseURL,
		Timeout: 5 * time.Second,
		Headers: make(map[string]string),
	}
}

// SetTimeOut 设置超时时间
func (h *HttpClient) SetTimeOut(timeout time.Duration) *HttpClient {
	h.Timeout = timeout
	return h
}

// SetHeader 设置请求头
func (h *HttpClient) SetHeader(key, value string) *HttpClient {
	h.Headers[key] = value
	return h
}

// SetHeaders 批量设置请求头
func (h *HttpClient) SetHeaders(headers map[string]string) *HttpClient {
	for k, v := range headers {
		h.Headers[k] = v
	}
	return h
}

func (h *HttpClient) Request(method, path string, body interface{}, result interface{}, options *RequestOptions) error {
	urlStr := h.BaseUrl + path

	// 设置查询参数
	if options != nil && options.QueryParams != nil {
		queryParams := url.Values{}
		for k, v := range options.QueryParams {
			queryParams.Add(k, v)
		}
		if encoded := queryParams.Encode(); encoded != "" {
			urlStr = urlStr + "?" + encoded
		}
	}

	var req *httplib.BeegoHTTPRequest
	switch method {
	case "GET":
		req = httplib.Get(urlStr)
	case "POST":
		req = httplib.Post(urlStr)
	case "PUT":
		req = httplib.Put(urlStr)
	case "DELETE":
		req = httplib.Delete(urlStr)
	default:
		return ErrInvalidMethod
	}

	// 设置超时
	timeout := h.Timeout
	if options != nil && options.Timeout > 0 {
		timeout = options.Timeout
	}
	req.SetTimeout(timeout, timeout)

	// 设置请求头
	headers := h.Headers
	if options != nil && options.Headers != nil {
		for k, v := range options.Headers {
			headers[k] = v
		}
	}

	for k, v := range headers {
		req.Header(k, v)
	}

	// 设置表单参数
	if options != nil && options.FormParams != nil {
		for k, v := range options.FormParams {
			req.Param(k, v)
		}
	}

	// 设置请求体
	if body != nil {
		jsonData, err := json.Marshal(body)
		if err != nil {
			return err
		}
		req.Body(jsonData)
		if _, exists := headers["Content-Type"]; !exists {
			req.Header("Content-Type", "application/json")
		}
	}

	// 输出请求内容
	logger.Debug(fmt.Sprintf("HTTP %s请求, url: %s, headers: %v, body: %v", method, urlStr, headers, body))

	// 发送请求
	resp, err := req.Response()

	if err != nil {
		return errors.New(fmt.Sprintf("HTTP %s请求失败, url: %s, error: %v", method, urlStr, err))
	}

	if resp.StatusCode >= 400 {
		return errors.New(fmt.Sprintf("HTTP %s请求返回错误状态码, url: %s, status: %d", method, urlStr, resp.StatusCode))
	}
	if result != nil {
		if err := req.ToJSON(result); err != nil {
			return errors.New(fmt.Sprintf("HTTP %s请求返回结果解析失败, url: %s, error: %v", method, urlStr, err))
		}
	}
	return nil
}

func (h *HttpClient) RequestWithRetry(method, path string, body interface{}, result interface{}, options *RequestOptions, maxRetries int) error {
	var err error
	for i := 0; i <= maxRetries; i++ {
		err := h.Request(method, path, body, result, options)
		if err == nil {
			return nil
		}

		if i < maxRetries {
			waitTime := time.Duration(i+1) * time.Second
			time.Sleep(waitTime)
			logger.Info(fmt.Sprintf("HTTP %s请求失败, url: %s, error: %v, 重试中, 第 %d 次", method, h.BaseUrl+path, err, i+1))
		}
	}
	return fmt.Errorf("HTTP %s请求失败, url: %s, error: %v, 重试次数已用尽", method, h.BaseUrl+path, err)
}
